package com.cn.lp.export.server.entity.executor;

import com.cn.lp.export.server.ExportResultCodes;
import com.cn.lp.export.server.base.context.ExportExceptionCreator;
import com.cn.lp.export.server.base.utils.TupleAide;
import com.cn.lp.export.server.entity.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by qirong on 2020/10/18.
 */
public abstract class BaseExportExecutor implements ExportExecutor {

    @Autowired
    protected ExportTaskDao exportTaskDao;

    @Autowired
    protected ExportDataDepository exportDataDepository;

    @Autowired
    private ExportSyncDataDao exportSyncDataDao;

    private Logger logger = LoggerFactory.getLogger(BaseExportExecutor.class);

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExportStatus export(long taskId) {
        ExportTask exportTask = exportTaskDao.findById(taskId)
            .orElseThrow(() -> ExportExceptionCreator.create(ExportResultCodes.TASK_NOT_EXIST, taskId));
        try {
            List<Map<String, Object>> dataList = exportDataDepository.getDataList(exportTask);
            Map<String, Object> excelHeaderMap = exportTask.getExportFieldMap();
            List<List<Object>> excelExportDataList = new ArrayList<>();
            int maxLayer = getMaxLayer(excelHeaderMap);
            Pair<List<String>, List<List<String>>> result = getHeaderFieldList(excelHeaderMap, maxLayer);
            List<String> dataFieldNamList = result.getLeft();
            dataList.stream().forEach(data -> {
                List<Object> rowData = new ArrayList<>();
                for (Object dataField : dataFieldNamList) {
                    if (data.get(dataField) == null) {
                        rowData.add("");
                    } else {
                        rowData.add(data.get(dataField));
                    }
                }
                excelExportDataList.add(rowData);
            });
            doExport(exportTask, excelExportDataList, result.getRight());
            exportTask.finish();
        } catch (Exception ex) {
            String message = taskId + "导出任务失败, " + ExceptionUtils.getStackTrace(ex);
            exportTask.fail(message.substring(0, Math.min(500, message.length())));
            logger.error(taskId + "导出任务失败", ex);
        }
        exportTaskDao.save(exportTask);
        afterExport(exportTask);
        return exportTask.getExportStatus();
    }

    protected abstract void doExport(ExportTask exportTask, List<List<Object>> excelExportDataList, List<List<String>> excelHeaderList);


    protected void afterExport(ExportTask exportTask) {
        exportSyncDataDao.deleteByTaskId(exportTask.getId());
    }

    /**
     * 计算Excel表头最大层数
     * 注：（最大层数+1）
     *
     * @param excelHeaderMap Excel表头map数据
     */
    private int getMaxLayer(Map<String, Object> excelHeaderMap) {
        int layer = 1;
        for (Map.Entry<String, Object> header : excelHeaderMap.entrySet()) {
            if (header.getValue() instanceof Map) {
                Map<String, Object> subHeader = (Map<String, Object>) header.getValue();
                layer = layer + getMaxLayer(subHeader);
            }
        }
        return layer;
    }

    /**
     * 获取Excel数据对应的字段列表
     * 同时生成表头数据
     *
     * @param excelHeaderMap Excel表头map数据
     * @param maxLayer       Excel表头最大层数
     * @return 字段列表, 表头数据
     */
    private Pair<List<String>, List<List<String>>> getHeaderFieldList(Map<String, Object> excelHeaderMap, int maxLayer) {
        //TODO 采用递归？
        //Excel字段列表
        List<String> dataFieldNamList = new ArrayList<>();
        List<List<String>> excelHeaderList = new ArrayList<>();
        int headLayer = maxLayer > 1 ? maxLayer : 1;
        for (Map.Entry<String, Object> header : excelHeaderMap.entrySet()) {
            //第一行表头
            if (header.getValue() instanceof String) {
                List<String> headColumn = new ArrayList<>();
                for (int i = 0; i < headLayer; i++) {
                    headColumn.add(header.getKey());
                }
                excelHeaderList.add(headColumn);
                dataFieldNamList.add(String.valueOf(header.getValue()));
            } else if (header.getValue() instanceof Map) {
                LinkedHashMap<String, Object> subHeader = (LinkedHashMap<String, Object>) header.getValue();
                int subLayer = headLayer - 1;
                for (Map.Entry<String, Object> subHeaderItem : subHeader.entrySet()) {
                    //第二行表头
                    if (subHeaderItem.getValue() instanceof String) {
                        List<String> subHeadColumn = new ArrayList<>();
                        subHeadColumn.add(header.getKey());
                        for (int i = 0; i < subLayer; i++) {
                            subHeadColumn.add(subHeaderItem.getKey());
                        }
                        dataFieldNamList.add(String.valueOf(subHeaderItem.getValue()));
                        excelHeaderList.add(subHeadColumn);
                    } else if (subHeaderItem.getValue() instanceof Map) {
                        LinkedHashMap<String, Object> subSubHeader = (LinkedHashMap<String, Object>) subHeaderItem.getValue();
                        int subSubLayer = subLayer - 1;
                        for (Map.Entry<String, Object> subSubHeaderItem : subSubHeader.entrySet()) {
                            //第三行表头
                            if (subSubHeaderItem.getValue() instanceof String) {
                                List<String> subSubHeadColumn = new ArrayList<>();
                                subSubHeadColumn.add(header.getKey());
                                subSubHeadColumn.add(subHeaderItem.getKey());
                                for (int i = 0; i < subSubLayer; i++) {
                                    subSubHeadColumn.add(subSubHeaderItem.getKey());
                                }
                                dataFieldNamList.add(String.valueOf(subSubHeaderItem.getValue()));
                                excelHeaderList.add(subSubHeadColumn);
                            } else if (subSubHeaderItem.getValue() instanceof Map) {
                                LinkedHashMap<String, Object> subSubSubHeader = (LinkedHashMap<String, Object>) subSubHeaderItem.getValue();
                                int subSubSubLayer = subSubLayer - 1;
                                for (Map.Entry<String, Object> subSubSubHeaderItem : subSubSubHeader.entrySet()) {
                                    //第四行表头
                                    if (subSubSubHeaderItem.getValue() instanceof String) {
                                        List<String> subSubSubheadColumn = new ArrayList<>();
                                        subSubSubheadColumn.add(header.getKey());
                                        subSubSubheadColumn.add(subHeaderItem.getKey());
                                        for (int i = 0; i < subSubSubLayer; i++) {
                                            subSubSubheadColumn.add(subSubHeaderItem.getKey());
                                        }
                                        subSubSubheadColumn.add(subSubSubHeaderItem.getKey());
                                        dataFieldNamList.add(String.valueOf(subSubSubHeaderItem.getValue()));
                                        excelHeaderList.add(subSubSubheadColumn);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return TupleAide.immutableOf(dataFieldNamList, excelHeaderList);
    }

}
